package com.reger.l2cache.core;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class Cache {

	private static final Logger log = LoggerFactory.getLogger(Cache.class);

	/**
	 * reids订阅发布时的更新缓存操作
	 */
	public static final String L2CACHE_SYNC_PUT_CHANNEL = "REGER.L2CACHE.SYNC.PUT";

	/**
	 * reids订阅发布时的删除缓存操作
	 */
	public static final String L2CACHE_SYNC_DEL_CHANNEL = "REGER.L2CACHE.SYNC.DEL";
	
	private final Cache cache;

	protected Cache(Cache cache) {
		this.cache = cache;
	}

	/**
	 * 从缓存获取数据
	 * 
	 * @param cacheKey
	 * @param timeOut
	 * @param returnType
	 * @return
	 */
	private Relust<?> getForCache(String cacheKey, long timeOut, Class<?> returnType) {
		try {
			while (true) {
				if (this.exist(cacheKey)) {
					try {
						return this.get(cacheKey, returnType);
					} catch (Exception e) {
						log.info("从缓存获取数据时出现异常，不再等待锁", e);
						return null;
					}
				} else if (this.tryLock(cacheKey, timeOut)) {
					log.debug("获取锁成功");
					return null;
				}
				TimeUnit.MILLISECONDS.sleep(10);
			}
		} catch (Exception e) {
			log.warn("获取缓存{}锁异常，不再等待锁", cacheKey, e);
			return null;
		}
	}

	/**
	 * 调用方法并缓存
	 * @param cacheKey
	 * @param timeOut
	 * @param method
	 * @param rootObject
	 * @param args
	 * @return
	 */
	private Relust<?> invokeCache(String cacheKey, long timeOut, Method method, Object rootObject, Object[] args) {
		try {
			Object value;
			if (cache != null) {
				value = cache.invoking(cacheKey, timeOut, method, rootObject, args);
			} else {
				value = method.invoke(rootObject, args);
			}
			try {
				this.put(cacheKey, value, timeOut);
			} catch (Exception e2) {
				log.warn("{}.{}响应结果写入缓存异常,cacheKey={}", rootObject.getClass(), method.getName(), cacheKey, e2);
			}
			if (value == null) {
				return new Relust<Object>();
			}
			return new Relust<Object>(value);
		} catch (Throwable e) {
			try {
				this.putException(cacheKey, e, timeOut);
			} catch (Exception e2) {
				log.warn("{}.{}响应结果异常时写入缓存异常,cacheKey={}", rootObject.getClass(), method.getName(), cacheKey, e2);
			}
			return new Relust<Object>(e);
		} finally {
			this.unLock(cacheKey);
		}
	}

	/**
	 * 调用过程
	 * @param cacheKey
	 * @param timeOut
	 * @param method
	 * @param rootObject
	 * @param args
	 * @return
	 * @throws Throwable
	 */
	public Object invoking(String cacheKey, long timeOut, Method method, Object rootObject, Object[] args)
			throws Throwable {
		Class<?> returnType = method.getReturnType();
		Relust<?> relust = this.getForCache(cacheKey, timeOut, returnType);
		if (relust == null) {
			relust = this.invokeCache(cacheKey, timeOut, method, rootObject, args);
		}
		if (relust.getIsNull()) {
			return null;
		}
		if (relust.getThrowable() == null) {
			return relust.getRelust();
		}
		throw relust.getThrowable();
	}

	/**
	 * 更新一个缓存
	 * 
	 * @param cacheKey
	 * @param timeOut
	 * @param value
	 */
	public void putData(String cacheKey, Object value, long timeOut) {
		this.put(cacheKey, value, timeOut);
		if (cache != null) {
			cache.putData(cacheKey, value, timeOut);
		}
	}

	/**
	 * 删除一个缓存
	 * 
	 * @param cacheKey
	 */
	public void removeData(String cacheKey) {
		this.remove(cacheKey);
		if (cache != null) {
			cache.removeData(cacheKey);
		}
	}

	/**
	 * 清空缓存
	 */
	public void clearData() {
		this.clear();
		if (cache != null) {
			cache.clearData();
		}
	}

	/**
	 * 对请求过程进行加锁
	 * 
	 * @param cacheKey
	 * @param timeOut
	 *            最多加锁的时间，可以多次加锁
	 */
	protected abstract boolean tryLock(String cacheKey, long timeOut);

	/**
	 * 释放当前线程持有的锁缓存锁，
	 * 
	 * @param cacheKey
	 */
	protected abstract void unLock(String cacheKey);

	/**
	 * 
	 * @param cacheKey
	 * @return
	 */
	protected abstract boolean exist(String cacheKey);

	/**
	 * 从缓存中获取,可能返回的是一个异常
	 * 
	 * @param cacheKey
	 * @param returnType
	 * @return
	 */
	protected abstract Relust<Object> get(String cacheKey, Class<?> returnType);

	/**
	 * 响应结果写入缓存
	 * 
	 * @param cacheKey
	 * @param value
	 * @param timeOut
	 */
	protected abstract void put(String cacheKey, Object value, long timeOut);

	/**
	 * 写入缓存
	 * 
	 * @param cacheKey
	 * @param e
	 */
	protected abstract void putException(String cacheKey, Throwable e, long timeOut);

	/**
	 * 删除一个缓存
	 * 
	 * @param cacheKey
	 */
	protected abstract void remove(String cacheKey);

	/**
	 * 清理缓存
	 */
	protected abstract void clear();
}
